﻿
interface JQueryStatic {

    IgnoreRequire(value?: any): any;

    MustRequire(value?: any): any;
}

interface JQuery {
    size(): any;
}

interface IValidationSet {
    error?: string,
    formGroup?: string,
    validationEventTriggers?: string,
    inlineValidation?: boolean,
    returnIsValid?: boolean,
    scroll?: boolean,
    unbindEngine?: boolean,
    ajaxSubmit?: boolean,
    success?: boolean,
    ignoreRequire?: boolean,
}



module validationEngine {


    var allRules: { [key: string]: { [key: string]: any; } };
    allRules = {
        required: {
            regex: "none",
            alertText: "必填",
            alertTextCheckboxMultiple: "请选择一个选项",
            alertTextCheckboxe: "必须选中"
        },
        length: {
            regex: "none",
            alertText: "长度在 ",
            alertText2: " 到 ",
            alertText3: " 个字符之间"
        },
        confirm: {
            regex: "none",
            alertText: "输入内容不匹配"
        },
        url: {
            regex:
            /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,
            alertText: "无效的网址"
        },
        idNumber: {
            regex: /^\d{17}([0-9]|X|x)$/,
            alertText: "身份证号码无效"
        },
        mobile: {
            regex: /^(1)[0-9]{10}$/,
            alertText: "无效手机号码"
        },
        email: {
            regex: /^[a-zA-Z0-9_.-]+@([a-zA-Z0-9-]+.)+[a-zA-Z0-9]{2,4}$/,
            alertText: "无效邮件地址"
        },
        integer: {
            regex: /^-?[0-9]+$/,
            alertText: "只允许录入整数"
        },
        pinteger: {
            regex: /^[0-9]+$/,
            alertText: "只允许录入整数"
        },
        pdecimal: {
            regex: /^\d*\.?\d+$/,
            alertText: "只能输入数字"
        },
        decimal: {
            regex: /^-?\d*\.?\d+$/,
            alertText: "只能输入数字"
        }
    }


    export var validationEngineLanguage = {
        allRules: allRules
    }


    //我们自定义的设置，主要是error、formGroup
    export var customSet: IValidationSet = {
        error: "has-error",
        formGroup: ".form-group-default",
        validationEventTriggers: "keyup blur",
        inlineValidation: true,
        returnIsValid: false,
        scroll: true,
        unbindEngine: false,
        ajaxSubmit: true,
        success: false,
        ignoreRequire: false
    };

    //不验证必填项
    $.IgnoreRequire = () => {
        customSet.ignoreRequire = true;
    }

    //必须验证必填项
    $.MustRequire = () => {
        customSet.ignoreRequire = false;
    }


    /**
     * 配置默认设置
     * @param set
     */
    export function SetConfig(set: any) {
        customSet = $.extend(customSet, set);
    }

    export var settings: IValidationSet = {

    }

    var isError: boolean = false;
    var showTriangle: boolean = true;

    function loadValidation(caller: any) {
        var rulesParsing = $(caller).attr("class");

        var rulesRegExp = /\[(.*)\]/;
        var getRules = rulesRegExp.exec(rulesParsing);
        var str = getRules[1];
        var pattern = /\[|,|\]/;
        var result = str.split(pattern);
        return validateCall(caller, result);
    }

    function validateCall(caller: any, rules?: any) {
        var promptText = "";
        var callerName = $(caller).attr("name");
        isError = false;
        showTriangle = true;
        var callerType = $(caller).attr("type");
        for (var i = 0; i < rules.length; i++) {
            switch (rules[i]) {
                case "optional":
                    if (!$(caller).val()) { return isError; }
                    break;
                case "required":
                    if (settings.ignoreRequire != true) required(caller);
                    break;
                case "custom":
                    customRegex(caller, rules, i);
                    break;
                case "length":
                    length(caller, rules, i);
                    break;
                case "confirm":
                    confirm(caller, rules, i);
                    break;
            }
        }
        radioHack();
        if (isError) {
            buildPrompt(caller, promptText);
        } else {
            var group = findTipsGroup(caller);
            group.removeClass(settings.error).prop("title", "");

        }
        function radioHack() {
            if ($("input[name='" + callerName + "']").size() > 1 && (callerType == "radio" || callerType == "checkbox")) {
                caller = $("input[name='" + callerName + "'][type!=hidden]:first");
                showTriangle = false;
            }
        }

        function required(caller: any) {
            callerType = $(caller).attr("type");
            switch ($(caller).prop("tagName").toLowerCase()) {
                case "textarea":
                    callerType = "textarea";
                    break;
                case "select":
                    callerType = "select";
                    break;
            }
            var types = ["hidden", "text", "password", "textarea", "select", "date", "number", "tel", "email", "url", "month", "week", "time", "datetime", "datetime-local", "search"];
            if (!callerType) callerType = "text";
            switch (callerType) {
                case "radio":
                case "checkbox":
                    callerName = $(caller).attr("name");
                    if ($("input[name='" + callerName + "']:checked").size() === 0) {
                        isError = true;
                        if ($("input[name='" + callerName + "']").size() === 1) {
                            promptText += validationEngine.validationEngineLanguage.allRules["required"]["alertTextCheckboxe"];
                        } else {
                            promptText += validationEngine.validationEngineLanguage.allRules["required"]["alertTextCheckboxMultiple"];
                        }
                    }
                    break;
                default:
                    if (types.indexOf(callerType) >= 0) {
                        var value = $(caller).val();

                        if (typeof (value) == "object") {
                            if (!value || value.length <= 0) isError = true;
                        }
                        else {
                            if (!value || !value.trim()) isError = true;
                        }

                        if (isError == true) {
                            promptText += validationEngine.validationEngineLanguage.allRules["required"]["alertText"];
                        }
                    }
            }
        }
        function customRegex(caller: any, rules?: any, position?: any) {
            var customRule: string = rules[position + 1];
            var pattern: RegExp = <RegExp>eval(<string>(validationEngine.validationEngineLanguage.allRules[customRule]["regex"]));
            if ($(caller).val() && !pattern.test($(caller).val())) {
                isError = true;
                var txt: string = validationEngine.validationEngineLanguage.allRules[customRule]["alertText"];
                promptText += txt;
            }
        }
        function confirm(caller: any, rules?: any, position?: any) {
            var confirmField = rules[position + 1];
            if ($(caller).val() !== $("#" + confirmField).val()) {
                isError = true;
                promptText += validationEngine.validationEngineLanguage.allRules["confirm"]["alertText"];
            }
        }
        function length(caller: any, rules?: any, position?: any) {
            var startLength = eval(rules[position + 1]);
            var endLength = eval(rules[position + 2]);
            var feildLength = $(caller).val().length;
            if (feildLength < startLength || feildLength > endLength) {
                isError = true;
                promptText += validationEngine.validationEngineLanguage.allRules["length"]["alertText"]
                    + startLength + validationEngine.validationEngineLanguage.allRules["length"]["alertText2"]
                    + endLength
                    + validationEngine.validationEngineLanguage.allRules["length"]["alertText3"];
            }
        }
        return isError ? true : false;
    }

    /**
        * 找到控件组名称，或者向上数三层容器
        * @param {} caller 
        * @returns {} 
        */
    function findTipsGroup(caller: any) {
        var group = $(caller).closest(".form-group-default");
        if (group.length === 0) group = $(caller).parent().parent();
        return group;
    }

    /**
         * 增加错误提示信息与错误颜色
         * @param {} caller 
         * @param {} promptText 
         * @param {} type 
         * @param {} ajaxed 
         * @returns {} 
         */
    function buildPrompt(caller: any, promptText?: any) {
        var group = findTipsGroup(caller);
        group.addClass(settings.error);
        group.prop("title", promptText);
        return $("<div>");
    }

    function submitValidation(caller: any) {
        var stopForm = false;
        var items = $(caller).find("[class*=validate]");
        items.each(function () {
            var validationPass = loadValidation(this);
            return validationPass ? stopForm = true : "";
        });
        return stopForm;
    }


    function setConfig(set: IValidationSet) {
        if (!settings) SetConfig({});
        settings = jQuery.extend(customSet, set);
    }

    $.fn.validationEngine = function (set: IValidationSet) {
        setConfig(set);
        if (settings.inlineValidation == true) {
            if (!settings.returnIsValid) {
                var target = "[class*=validate][type!=checkbox]";

                $(this).on(settings.validationEventTriggers, target, function () {
                    var s = loadValidation(this);
                });

                $(this).on("click", target, function () {
                    loadValidation(this);
                });
            }
        }
        if (settings.returnIsValid) {
            return !submitValidation(this);
        }
    };


}





